Android — AsyncTask 异步加载

前言

我们知道,当Android程序启动的时候,会同时启动一个对应的主线程 (Main Thread),这个线程主要负责处理与UI相关的事件,我们也叫它UI线程。而Android UI操作并不是线程安全的并且这些操作都要在UI线程中执行。还有就是像一些耗时操作如网络操作不能在主线程中进行,一旦时间超过五秒,就会报ANR(应用程序无响应)异常。那么问题来了,如果我们要在非UI线程中操作UI该怎么做呢?这里就需要引入异步任务了,当然实现异步任务也不只是可以使用AsyncTask,这里我们先讲讲AsyncTask这个官方给我们封装好的轻量级异步类。

基本结构

AsyncTask是一个抽象类,一般我们会定义一个类继承至AsyncTask,然后重写相关的方法。

AsyncTask 包含三个泛型参数:(不需要返回类型写 Void 即可)

Params : 启动任务时输入的参数类型。

Progress : 后台任务执行返回进度值得类型。

Result : 后台任务执行完成后返回结果的类型。

相关方法与执行流程

实例演示

  • 下载图片

从网络上下载一张图片并更新到UI

  • 进度条演示

模拟下载任务进度实时更新进度条

布局:

主页面两个按钮,点击分别切换到显示图片布局和显示进度条布局。

代码:

MainActivity 中两个按钮的点击事件:

1
2
3
4
5
6
7
public void loadImage(View view) {
startActivity(new Intent(this, ImageTest.class));
}
public void loadProgress(View view) {
startActivity(new Intent(this,ProgressBarTest.class));
}

ImageTest.java :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package top.omooo.admin.myasynctask;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* Created by Omooo on 2017/8/9.
*/
public class ImageTest extends Activity {
private ImageView imageView;
private ProgressBar progressBar;
private static String URL="http://img.my.csdn.net/uploads/201504/12/1428806103_9476.png";
//
// public ImageTest(ImageView imageView) {
// this.imageView = imageView;
// }
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
imageView = findViewById(R.id.imageView1);
progressBar = findViewById(R.id.progressBar1);
//设置传递进去的参数
new MyAsyncTask().execute(URL);
}
class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected void onPreExecute() {
//做初始化操作
super.onPreExecute();
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//操作UI
progressBar.setVisibility(View.GONE);
imageView.setImageBitmap(bitmap);
}
@Override
protected Bitmap doInBackground(String... stings) {
//可变长数组获取传递进来的参数
String url=stings[0];
Bitmap bitmap = null;
URLConnection connection;
InputStream inputStream;
try {
connection = new URL(url).openConnection();
inputStream = connection.getInputStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//通过decodeStream解析输入流
bitmap = BitmapFactory.decodeStream(bufferedInputStream);
inputStream.close();
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}
}

ProgressBar.java :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package top.omooo.admin.myasynctask;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.widget.ProgressBar;
/**
* Created by Omooo on 2017/8/9.
*/
public class ProgressBarTest extends Activity {
private ProgressBar progressBar;
private MyAsyncTask mTask;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar);
progressBar = findViewById(R.id.progressBar);
//启动异步任务
mTask = new MyAsyncTask();
mTask.execute();
}
@Override
protected void onPause() {
super.onPause();
if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
//cancel()只是将对于的AsyncTask标记为cancel状态,并不是真正的取消线程
mTask.cancel(true);
}
}
class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
@Override
protected Void doInBackground(Void... voids) {
for (int i = 0; i < 100; i++) {
if (isCancelled()) {
break;
}
publishProgress(i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (isCancelled()) {
return;
}
progressBar.setProgress(values[0]);
}
}
}

关于取消任务:

在调用cancel()方法时,如果任务正在进行,即doInBackground()还在运行,这时取消并不会影响doInBackground()方法的执行,只是不会调用doPostExecute()方法而已,而且就算是调用了pubilshProgress(),onProgressUpdate()也不会执行,所以不是真正的取消操作,只是取消了在UI主线程的操作,不会调用doPostExecute()和onProgressUpdate()方法。所以如果真的想取消任务,需要在onPause()方法中添加:

1
2
3
4
if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
//cancel()只是将对于的AsyncTask标记为cancel状态,并不是真正的取消线程
mTask.cancel(true);
}

注意事项:

  1. 必须在UI线程中创建AsyncTask实例,而且也只能在UI线程中调用AsyncTask的execute()方法。
  2. 重写的四个方法是系统自动调用的,不应该手动调用。
  3. 每个AsyncTask只能被执行一次

参考

AsyncTask 文档

慕课网 Android — AsyncTask 基础

我们一直都向往,面朝大海,春暖花开。 但是几人能做到,心中有爱,四季不败?